#pragma once
#ifndef __HWINSYNCH_H__
#define __HWINSYNCH_H__

#include "hwinhandle.h"
#include "hwinsecurity.h"
#include "hwindatetime.h"
#include "hwinexception.h"


namespace harlinn
{
    namespace windows
    {
        class Timeout
        {
        public:
            static const DWORD Infinite = INFINITE;
        };


        class AbandonedMutexException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( AbandonedMutexException );
        };


        class WaitableHandle : public SystemHandle
        {
        public:
            typedef SystemHandle Base;
            static const int WaitTimeout = -1;
        protected:
            HWIN_EXPORT static void ThrowAbandonedMutexException();
            
            explicit WaitableHandle(LPVOID theHandle, bool closeHandle = true)
                : Base(theHandle, closeHandle)
            {
                HWIN_TRACE();
            }

        protected:
            WaitableHandle()
            {
                HWIN_TRACE();
            }

            HWIN_EXPORT static int WaitForAnyOf(size_t numberOfHandles,HANDLE* handles,DWORD timeoutInMillis);
            HWIN_EXPORT static int WaitForAnyOf(size_t numberOfHandles,HANDLE* handles,DWORD timeoutInMillis,bool alertable);
            HWIN_EXPORT static int WaitForAllOf(size_t numberOfHandles,HANDLE* handles,DWORD timeoutInMillis);
            HWIN_EXPORT static int WaitForAllOf(size_t numberOfHandles,HANDLE* handles,DWORD timeoutInMillis,bool alertable);

        public:
            HWIN_EXPORT bool Wait();
            HWIN_EXPORT bool Wait(DWORD timeoutInMillis);
            HWIN_EXPORT bool Wait(const TimeSpan& timeout);

            HWIN_EXPORT bool Wait(bool alertable);
            HWIN_EXPORT bool Wait(DWORD timeoutInMillis, bool alertable);
            HWIN_EXPORT bool Wait(const TimeSpan& timeout, bool alertable);
            

            HWIN_EXPORT static int WaitForAnyOf(const std::vector< std::shared_ptr< WaitableHandle > >& waitableHandles);
            HWIN_EXPORT static int WaitForAnyOf(const std::vector< std::shared_ptr< WaitableHandle > >& waitableHandles,bool alertable);
            HWIN_EXPORT static int WaitForAllOf(const std::vector< std::shared_ptr< WaitableHandle > >& waitableHandles);
            HWIN_EXPORT static int WaitForAllOf(const std::vector< std::shared_ptr< WaitableHandle > >& waitableHandles,bool alertable);

            HWIN_EXPORT static int WaitForAnyOf(const std::vector< std::shared_ptr< WaitableHandle > >& waitableHandles,DWORD timeoutInMillis);
            HWIN_EXPORT static int WaitForAnyOf(const std::vector< std::shared_ptr< WaitableHandle > >& waitableHandles,DWORD timeoutInMillis,bool alertable);
            HWIN_EXPORT static int WaitForAllOf(const std::vector< std::shared_ptr< WaitableHandle > >& waitableHandles,DWORD timeoutInMillis);
            HWIN_EXPORT static int WaitForAllOf(const std::vector< std::shared_ptr< WaitableHandle > >& waitableHandles,DWORD timeoutInMillis,bool alertable);

        };


        enum class EventWaitHandleRights : DWORD
        {
            Modify               = EVENT_MODIFY_STATE,
            Delete               = DELETE,
            ReadPermissions      = READ_CONTROL,
            ChangePermissions    = WRITE_DAC, 
            TakeOwnership        = WRITE_OWNER,
            Synchronize          = SYNCHRONIZE,
            FullControl          = EVENT_ALL_ACCESS
        };
        DEFINE_ENUM_FLAG_OPERATORS(EventWaitHandleRights)

        class EventWaitHandle : public WaitableHandle
        {
        public:
            typedef WaitableHandle Base;
        protected:

            HWIN_EXPORT static HANDLE CreateEvent(SECURITY_ATTRIBUTES* eventAttributes, wchar_t* name, bool initiallySet, bool manualReset, EventWaitHandleRights desiredAccess);
            HWIN_EXPORT static HANDLE Open(wchar_t* name,EventWaitHandleRights desiredAccess, bool inheritHandle);

        public:
            explicit EventWaitHandle(HANDLE theHandle, bool closeHandle = true)
                : Base(theHandle, closeHandle)
            {
                HWIN_TRACE();
            }

            HWIN_EXPORT EventWaitHandle(SECURITY_ATTRIBUTES* eventAttributes, wchar_t* name, bool initiallySet, bool manualReset, EventWaitHandleRights desiredAccess = EventWaitHandleRights::FullControl);
            HWIN_EXPORT EventWaitHandle(wchar_t* name, bool initiallySet, bool manualReset, EventWaitHandleRights desiredAccess = EventWaitHandleRights::FullControl);
            HWIN_EXPORT EventWaitHandle(bool initiallySet, bool manualReset, EventWaitHandleRights desiredAccess = EventWaitHandleRights::FullControl);

            HWIN_EXPORT static std::shared_ptr<EventWaitHandle> OpenExisting(wchar_t* name, EventWaitHandleRights desiredAccess = EventWaitHandleRights::Modify | EventWaitHandleRights::Synchronize, bool inheritHandle = false);


            HWIN_EXPORT bool Set();
            HWIN_EXPORT bool Reset();
        };

        class AutoResetEvent : public EventWaitHandle
        {
        public:
            typedef EventWaitHandle Base;

            HWIN_EXPORT AutoResetEvent(bool initiallySet);
        };


        class ManualResetEvent : public EventWaitHandle
        {
        public:
            typedef EventWaitHandle Base;

            HWIN_EXPORT ManualResetEvent(bool initiallySet);
        };

        enum class MutexRights
        { 
            Modify               = 0x000001,
            Delete               = 0x010000,
            ReadPermissions      = 0x020000,
            ChangePermissions    = 0x040000, 
            TakeOwnership        = 0x080000,
            Synchronize          = 0x100000, 
            FullControl          = 0x1F0001 
        };
        DEFINE_ENUM_FLAG_OPERATORS(MutexRights)

        class Mutex : public WaitableHandle
        {

            HWIN_EXPORT static HANDLE Create( SECURITY_ATTRIBUTES* eventAttributes, wchar_t* name, bool initiallyOwned, MutexRights desiredAccess );
            HWIN_EXPORT static HANDLE Open(wchar_t* name, MutexRights desiredAccess = MutexRights::Modify | MutexRights::Synchronize , bool inheritHandle = false);
        public:
            typedef WaitableHandle Base;

            explicit Mutex(HANDLE theHandle, bool closeHandle = true)
                : Base(theHandle, closeHandle)
            {
                HWIN_TRACE();
            }

            HWIN_EXPORT Mutex( SECURITY_ATTRIBUTES* eventAttributes, wchar_t* name, bool initiallyOwned, MutexRights desiredAccess = MutexRights::FullControl );
            HWIN_EXPORT Mutex( wchar_t* name, bool initiallyOwned , MutexRights desiredAccess = MutexRights::FullControl );
            HWIN_EXPORT Mutex( bool initiallyOwned , MutexRights desiredAccess = MutexRights::FullControl );

            HWIN_EXPORT static std::shared_ptr<Mutex> OpenExisting(wchar_t* name, MutexRights desiredAccess = MutexRights::Modify | MutexRights::Synchronize , bool inheritHandle = false);

            HWIN_EXPORT Mutex& Release();
        };

        class CriticalSection : public Object
        {
            CRITICAL_SECTION criticalSection;
        public:
            HWIN_EXPORT CriticalSection();
            HWIN_EXPORT CriticalSection(DWORD spinCount);
            HWIN_EXPORT bool Wait();
            HWIN_EXPORT bool TryWait();
            HWIN_EXPORT CriticalSection& Release();
        };


        template<typename T>
        class Lock 
        {
            T& waitable;
        public:
            Lock(T& theWaitable)
                : waitable(theWaitable)
            {
                waitable.Wait();
            }

            ~Lock()
            {
                waitable.Release();
            }
        };

        template< >
        class Lock < std::shared_ptr< Mutex > >
        {
            std::shared_ptr< Mutex > mutex;
        public:
            Lock(std::shared_ptr< Mutex > theMutex)
                : mutex(theMutex)
            {
                mutex->Wait();
            }
            ~Lock()
            {
                mutex->Release();
            }
        };

        template<  >
        class Lock < std::shared_ptr< CriticalSection > >
        {
            std::shared_ptr< CriticalSection > criticalSection;
        public:
            Lock(std::shared_ptr< CriticalSection > theCriticalSection)
                : criticalSection(theCriticalSection)
            {
                criticalSection->Wait();
            }
            ~Lock()
            {
                criticalSection->Release();
            }
        };




    };

};

#endif //__HWINSYNCH_H__
